from random import uniform
import random
import copy

# arquivo com o dataset
fileInput = "zooDataset.data"

def learn(instances):
    errorRateList = []

    # Parametros da RNA, inicializacao
    w = []
    for i in range(10):
        w.append(uniform(-1., 1.))
    teta = uniform(-1., 1.)

    # Parametros do algoritmo regra delta, inicializacao
    passo = 0.1
    maxTotalError = 0.1
    numIter = 1000

    totalError = 1
    iter = 0
    # laco principal
    while (totalError > maxTotalError) and (iter < numIter):
        # inicializacao do erro total desta iteracao
        totalError = 0
        dataset = copy.copy(instances)
        # embaralhando o dataset
        random.shuffle(dataset)
        # para cada instancia do dataset
        for i in range(len(dataset)):
            (instance, classe) = dataset[i]
            # calculo do erro (y - y_desejado)
            error = calculateY(w, instance, teta) - classe

            for j in range(len(instance)):
                # ajuste dos pesos
                delta = passo * error * instance[j]
                w[j] -= delta
            # ajuste do limiar de disparo
            teta += passo * error
            # acumulando o erro
            totalError += abs(error)
        # salvando a taxa de erro da iteracao para construcao da
        # curva de aprendizagem
        errorRateList.append((iter, 1.0 - test(w, teta, instances)))
        iter += 1
    return (w, teta, errorRateList)

def test(w, teta, instances):
    accuracy = .0
    # para cada instancia do dataset
    for i in range(len(instances)):
        (instance, classe) = instances[i]
        # se a classe retornada pela RNA for o mesmo
        # da classe real, entao temos um acerto
        if classe == calculateY(w, instance, teta):
            accuracy += 1
    # a acuracia e a proporcao de acertos 
    # pelo total predito
    return accuracy / float(len(instances))
        

def calculateY(w,x,lim):

    sum = 0
    # para cada valor da instancia
    for i in range(len(w)):
        # pondera o valor de 
        # entrada com o peso w[i]
        sum += (w[i] * x[i])
    # subtrai-se o limiar de disparo
    sum -= lim
    
    # Funcao de Heavyside
    if sum < 0.0:
       return 0.0
    else:
       return 1.0

# Esta funcao faz a leitura do dataset de um
# arquivo TXT. Seu retorno eh uma lista de 
# instancias, onde cada instancia eh uma dupla:
# uma lista de atributos e uma classe
def loadDataset(pathNameFile):
    instances = []
    res = []
    for line in open(pathNameFile):
        t = line.split(",")
        instance = map(float, t)
        classe = instance.pop(len(instance) - 1)
        instances.append((instance, classe))
    
    return instances

# Implementa a tecnica de validacao cruzada.
# Ela retorna um conjunto de duplas, a saber:
# dataset de treino e o de teste.
def computeCrossValidationSet(instances):
    bases = []
    size = len(instances)
    trainingSetLength = int(0.9 * float(size))
    testingSetLength = int(0.1 * float(size))
    if (trainingSetLength + testingSetLength) != size:
        testingSetLength += 1
    testingSetIndex = 0
    for i in range(10):
        testing = []
        for j in range(testingSetLength):
            testing.append(instances[(testingSetIndex + j) % size])
        minTest = testingSetIndex
        maxTest = testingSetIndex + testingSetLength
        testingSetIndex += testingSetLength
        training = []
        for j in range(size):
            if (j < minTest) or (j >= maxTest):
                training.append(instances[j])
        bases.append((training, testing))
    return bases


def main():
    
    dataset = loadDataset(fileInput)
    foldsSet = computeCrossValidationSet(dataset)
    errorCurve = []
    accuracyList = []
    totalAccuracy = 0
    folds = len(foldsSet)

    for i in range(folds):
        (training, testing) = foldsSet[i]
        (w, teta, errorRateList) = learn(training)
        errorCurve.append(errorRateList)
        accuracy = test(w, teta, testing)
        totalAccuracy += accuracy
        accuracyList.append(accuracy)
    accuracyList.append(totalAccuracy / float(folds))

    salveResults("out", errorCurve, accuracyList)

def salveResults(fileName, errorCurve, accuracyList):
    
    for i in range(len(errorCurve)):
        f = open(fileName + str(i) + ".txt",'w')
        round = errorCurve[i]
        for j in range(len(round)):
            (iter, error) = round[j]
            f.write(str(iter) + ";" + str(error) + "\n")
        f.close
    f = open(fileName + "acc.txt", 'w')
    for i in range(len(accuracyList)):
        f.write(str(i) + ";" + str(accuracyList[i]) + "\n")
    f.close

if __name__ == "__main__":
    main()
